package top.babyyan.redis.util;

import org.hibernate.service.spi.ServiceException;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;

import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;

/**
 * 功能模块-分布式锁
 * @author superbabyyan
 * @since 2024/9/7 2:04
 */
@Component
public class RedisDistributedLock {
    private final static String LOCK_KEY = "LOCK_KEY:";

    private final   RedissonClient redissionClient;

    public RedisDistributedLock(RedissonClient redissionClient) {
        this.redissionClient = redissionClient;
    }


    // =============================分布式锁实现=============================

    /**
     *  无出参代码块
     * @param key
     */
    public void lock(String key, Runnable task) {
        redissionClient.getLock(LOCK_KEY + key).lock();
        try {
            task.run();
        } finally {
            redissionClient.getLock(LOCK_KEY + key).unlock();
        }
    }

    /**
     *  有出参代码块
     * @param key
     * @return
     */
    public <T> T lockAndReturn(String key, Callable<T> task) {
        redissionClient.getLock(LOCK_KEY + key).lock();
        try {
            try {
                return task.call();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        } finally {
            redissionClient.getLock(LOCK_KEY + key).unlock();
        }
    }

    public void execForWait(String lockKey, long waitTimeMs, Consumer<Void> consumer) {
        RLock lock = redissionClient.getLock(lockKey);
        try {
            boolean locked = lock.tryLock(waitTimeMs, TimeUnit.MILLISECONDS);
            if(!locked) {
                throw new ServiceException( "获取锁失败 lockKey=" + lockKey + "获取锁失败");
            }
            consumer.accept(null);
        } catch (InterruptedException e) {
            throw new ServiceException("获取锁失败");
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    /**
     *  不建议使用，需要手动释放锁
     * @param key
     * @param waitTime  等待时间
     * @param leaseTime 持有锁的时间
     * @return
     */
    public boolean tryLock(String key, long waitTime, long leaseTime) {
        try {
            return redissionClient.getLock(LOCK_KEY + key).tryLock(waitTime, leaseTime, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}
